package domain

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"gluenet/modules/manager/server/domain/dependency"
	"gluenet/modules/manager/server/infra"
	"gluenet/modules/manager/server/infra/prepare"
)

func init() {
	//fmt.Printf("domina init\n")
	prepare.RegisterService(func() {
		index := NewIndexDomain()
		prepare.InjectDependency(func() *Index {
			return index
		})
	})
}

type Index struct {
	IndexRepo  dependency.IndexRepo
	DetailRepo dependency.DetailRepo
}

func NewIndexDomain() *Index {
	domain := &Index{}
	prepare.FetchDependency(&domain.IndexRepo)
	prepare.FetchDependency(&domain.DetailRepo)
	return domain
}

func (r *Index) Put(user_id string, data interface{}) error {
	index, err := r.IndexRepo.Get(user_id)
	if err != nil {
		return err
	}

	lines, ok := data.([]interface{})
	if !ok {
		return fmt.Errorf("data is not []interface")
	}

	idm := make(map[string]bool, len(lines))
	for _, line := range lines {
		bts, _ := json.Marshal(line)
		d := md5.Sum(bts)
		id := hex.EncodeToString(d[:])
		if _, ok = idm[id]; ok {
			return fmt.Errorf("duplicate line")
		}
		idm[id] = true
	}

	return r.DetailRepo.PutDetailKV(index.DetailRepoKey, map[string]interface{}{
		"idmap": idm,
		"list":  lines,
	})
}

func (r *Index) Add(user_id string, line interface{}) error {
	index, err := r.IndexRepo.Get(user_id)
	if err != nil {
		return err
	}

	detailsi, err := r.DetailRepo.GetByRepoKey(index.DetailRepoKey)
	if err != nil {
		return err
	}

	details, ok := detailsi.(map[string]interface{})
	if !ok {
		return fmt.Errorf("detail is not map[string]interface")
	}

	idm, ok := details["idmap"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("detail is not contain id map")
	}
	list, ok := details["list"].([]interface{})
	if !ok {
		return fmt.Errorf("detail is not contain list")
	}

	bts, _ := json.Marshal(line)
	d := md5.Sum(bts)
	id := hex.EncodeToString(d[:])
	if _, ok = idm[id]; ok {
		return fmt.Errorf("line is exist")
	}
	idm[id] = true
	list = append(list, line)
	details["list"] = list

	return r.DetailRepo.PutDetailKV(index.DetailRepoKey, details)
}

func (r *Index) Get(user_id string) (interface{}, error) {
	index, err := r.IndexRepo.Get(user_id)
	if err != nil {
		return nil, err
	}

	detailsi, err := r.DetailRepo.GetByRepoKey(index.DetailRepoKey)
	if err != nil {
		return nil, err
	}

	details, ok := detailsi.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("detail is not map[string]interface")
	}

	list, ok := details["list"].([]interface{})
	if !ok {
		return nil, fmt.Errorf("detail is not contain list")
	}

	return list, nil
}

func (r *Index) ReSet(user_id string) error {
	index, err := r.IndexRepo.Get(user_id)
	if err != nil {
		return err
	}

	return r.DetailRepo.PutDetailKV(index.DetailRepoKey, map[string]interface{}{
		"idmap": map[string]interface{}{},
		"list":  []interface{}{},
	})
}

func (r *Index) NewIndex(userId string) error {
	return r.DetailRepo.PutDetailKV(fmt.Sprintf(infra.IndexRepoKeyFormat, userId), map[string]interface{}{})
}
